
#include "DftShowDbe.h"

#include "data.h"

#include <malloc.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/extensions/XShm.h> //Shared memory extension
#include <X11/extensions/Xdbe.h> //Double buffering extension
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/time.h>

using namespace std;


static Display *d;
static int s;
static Visual *v;
static Window w;
static XWindowAttributes wa;
static GC gc;
static Colormap cmap;
static XColor bufXColGrays[255];
static int wCurY;

static XImage *ximg;
static XShmSegmentInfo *xshminfo;

static XdbeBackBuffer wdbe;
static XdbeSwapInfo wdbeinfo;

static void openWindow()
{
    d = XOpenDisplay(NULL);
    s = DefaultScreen(d);
    v = DefaultVisual(d, s);
    wCurY = 0;
    cout << "Visual bits/rgb " << v->bits_per_rgb << endl;

    w = XCreateSimpleWindow(d, RootWindow(d, s), 0, 0, 600, 400, 0, BlackPixel(d, s), BlackPixel(d, s));
    XSetStandardProperties(d, w,"paDft","~ pa DFT ~",None,NULL,0,NULL);
    XSelectInput(d, w, ExposureMask|ButtonPressMask|KeyPressMask);
    XClearWindow(d, w);
    XMapRaised(d, w);
    XFlush(d);

    XGetWindowAttributes(d, w, &wa);
    XFlush(d);
    cout << "w depth " << wa.depth << endl;

    XGCValues gcvalues;
    gcvalues.function = GXcopy;
    gcvalues.plane_mask = AllPlanes;
    gcvalues.foreground = BlackPixel(d, s);
    gcvalues.background = WhitePixel(d, s);
    gc = XCreateGC(d, w, GCFunction|GCPlaneMask|GCForeground|GCBackground, &gcvalues);

    cmap = DefaultColormap(d, 0);
    for(int i=0; i<255; i++)
    {
        bufXColGrays[i].pixel=i;
        bufXColGrays[i].flags=DoRed|DoGreen|DoBlue;
        bufXColGrays[i].red=i*256;
        bufXColGrays[i].blue=i*256;
        bufXColGrays[i].green=i*256;

        XAllocColor(d, cmap, bufXColGrays+i);
    }
    XFlush(d);

    wdbe = XdbeAllocateBackBufferName(d, w, XdbeUndefined);
    wdbeinfo.swap_action = XdbeUndefined;
    wdbeinfo.swap_window = w;
    cout << "BackBuffer " << wdbe << endl;

    Bool bRes = XShmQueryExtension(d);
    cout << "XShm " << bRes << endl;
    int major, minor;
    XShmQueryVersion(d, &major, &minor, &bRes);
    cout << "XShm " << major << "." << minor << " Pixmaps " << bRes << endl;

    xshminfo = (XShmSegmentInfo*) malloc(sizeof(XShmSegmentInfo));
    ximg = XShmCreateImage(d, v, wa.depth/*dept*/, ZPixmap/*format*/, NULL /*data*/, xshminfo, wa.width, wa.height);
    cout << "XShmImage " << ximg << " " << ximg->width << " x " << ximg->height << " bits/pixel " << ximg->bits_per_pixel << " size " << (ximg->bytes_per_line * ximg->height) << endl;

    xshminfo->shmid = shmget(IPC_PRIVATE, ximg->bytes_per_line * ximg->height, IPC_CREAT|0777);
    cout << "xshminfo->shmid  " << xshminfo->shmid  << endl;

    xshminfo->shmaddr = (char *) shmat(xshminfo->shmid, 0, 0);
    xshminfo->readOnly = False;
    ximg->data = xshminfo->shmaddr;
    cout << "xshminfo->shmaddr " << ((void*)xshminfo->shmaddr) << endl;

    Status sRes;
    sRes = XShmAttach(d, xshminfo);
    cout << "XShmAttach " << sRes << endl;

    XFlush(d);

}

static void drawLineAndScroll(double *DftModData, int DftModDataLen)
{
    int y = ximg->height -1;
    memmove(ximg->data, ximg->data + ximg->bytes_per_line, ximg->bytes_per_line*y);

/*
    for (int x=0; x<ximg->width; x++)
    {
        //XPutPixel(ximg, i, y, bufXColGrays[254].pixel);
        int pos = y * ximg->bytes_per_line + x * ximg->bits_per_pixel /8;
        ximg->data[pos + 0] = 255*rand(); // blue
        ximg->data[pos + 1] = 255*rand(); // green
        ximg->data[pos + 2] = 0; //255*rand(); // yellow
    }
*/
    const int NumsamplesPerPixel = DftModDataLen / wa.width;
    int iData = 0;

    for (int i=0; i<wa.width && iData<DftModDataLen; i++)
    {
        double val = 0;
        double count = 0;
        for (int is=0;is<NumsamplesPerPixel && iData<DftModDataLen; is++, iData ++)
        {
            val += DftModData[iData];
            count ++;
        }
        val = val / count * 2;

        int iCol = max(0, (int)min(254, (int)(val*256)));
        //long pixel = bufXColGrays[iCol].pixel;

        int pos = y * ximg->bytes_per_line + i * ximg->bits_per_pixel /8;
        ximg->data[pos + 0] = iCol; // blue
        ximg->data[pos + 1] = iCol; // green
        ximg->data[pos + 2] = 0;//iCol; //255*rand(); // yellow
        //ximg->data[pos + 3] = 255;
    }

    /*
    XShmPutImage(d, wdbe, gc, ximg, 0, 0, 0, 0, ximg->width, ximg->height, True);
    XdbeSwapBuffers(d, &wdbeinfo, 1);
    XFlush(d);
    */
    XShmPutImage(d, w, gc, ximg, 0, 0, 0, 0, ximg->width, ximg->height, True);
    XFlush(d);

    /*
    XEvent event;
    while (true)
    {
        XNextEvent(d, &event);
        if (event.type == 77)
            break;
    }
    */
    XSync(d, True);
}

static void drawLine(double *DftModData, int DftModDataLen)
{
    const int NumsamplesPerPixel = DftModDataLen / wa.width;
    int iData = 0;

    for (int i=0; i<wa.width && iData<DftModDataLen; i++)
    {
        double val = 0;
        double count = 0;
        for (int is=0;is<NumsamplesPerPixel && iData<DftModDataLen; is++, iData ++)
        {
            val += DftModData[iData];
            count ++;
        }
        val = val / count * 1.5;

        int iCol = max(0, (int)min(254, (int)(val*256)));
//        long pixel = bufXColGrays[iCol].pixel;

        int pos = wCurY * ximg->bytes_per_line + i * ximg->bits_per_pixel /8;
        ximg->data[pos + 0] = iCol; // blue
        ximg->data[pos + 1] = iCol; // green
        ximg->data[pos + 2] = iCol;//iCol; //255*rand(); // yellow
        ximg->data[pos + 3] = 0;//iCol; //255*rand(); // yellow
    }

    for (int j=1; wCurY+j < wa.height && j<5; j++)
    {
        for (int i=0; i<wa.width && iData<DftModDataLen; i++)
        {
            int pos = (wCurY+j) * ximg->bytes_per_line + i * ximg->bits_per_pixel /8;
            ximg->data[pos + 0] = 0; // blue
            ximg->data[pos + 1] = 0; // green
            ximg->data[pos + 2] = 20;// red
            ximg->data[pos + 3] = 0;//iCol; //255*rand(); // yellow
        }
    }

    XShmPutImage(d, wdbe, gc, ximg, 0, 0, 0, 0, ximg->width, ximg->height, True);
    XdbeSwapBuffers(d, &wdbeinfo, 1);
    XFlush(d);

    wCurY++;
    if (wCurY >= wa.height)
        wCurY = 0;
}


void *thr_show_dft_results_dbe(void *UserData)
{
    my_pa_data *paData = (my_pa_data *) UserData;
    SynchroQueue *queDftData = paData->queDftMod;
    long uSecToWait = paData->DftMsecs * 1000 / 2;

    openWindow();

    struct timeval ts;
    struct timeval tsnow;
    gettimeofday(&ts, NULL);

    while (!queDftData->getSourceIsFinished() || queDftData->getSize() > 0)
    {
        void *pRawData = queDftData->get();
        if (pRawData == NULL)
        {
            usleep(100);
            continue;
        }
        /*
        gettimeofday(&tsnow, NULL);
        long usWaited = (tsnow.tv_sec - ts.tv_sec)*1000*1000 + (tsnow.tv_usec - ts.tv_usec);
        if (usWaited < uSecToWait)
        {
            long rem = uSecToWait - usWaited - 500;
            usleep(max((long)0, rem));
        }
        gettimeofday(&ts, NULL);
        */
        drawLine((double *) pRawData, paData->dft->getDftWindow_NumFreqSamples());
        free(pRawData);
    }

    XFreeGC(d, gc);
	XDestroyWindow(d, w);
	XCloseDisplay(d);
    return NULL;
}
